home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume10 / screen / part02 < prev    next >
Encoding:
Internet Message Format  |  1987-08-06  |  50.3 KB

  1. Path: uunet!rs
  2. From: rs@uunet.UU.NET (Rich Salz)
  3. Newsgroups: comp.sources.unix
  4. Subject: v10i096:  BSD multi-screen manager, Part02/02
  5. Message-ID: <788@uunet.UU.NET>
  6. Date: 7 Aug 87 11:15:44 GMT
  7. Organization: UUNET Communications Services, Arlington, VA
  8. Lines: 2048
  9. Approved: rs@uunet.UU.NET
  10.  
  11. Submitted-by: Oliver Laumann <seismo!tub!net>
  12. Posting-number: Volume 10, Issue 96
  13. Archive-name: screen/Part02
  14.  
  15. #! /bin/sh
  16. # This is a shell archive, meaning:
  17. # 1. Remove everything above the #! /bin/sh line.
  18. # 2. Save the resulting text in a file.
  19. # 3. Execute the file with /bin/sh (not csh) to create the files:
  20. #    screen.1
  21. #    screen.c
  22. export PATH; PATH=/bin:$PATH
  23. echo shar: extracting "'screen.1'" '(17895 characters)'
  24. if test -f 'screen.1'
  25. then
  26.     echo shar: will not over-write existing file "'screen.1'"
  27. else
  28. cat << \SHAR_EOF > 'screen.1'
  29. .if n .ds Q \&"
  30. .if n .ds U \&"
  31. .if t .ds Q ``
  32. .if t .ds U ''
  33. .TH SCREEN 1 "2 March 1987"
  34. .UC 4
  35. .SH NAME
  36. screen \- screen manager with VT100/ANSI terminal emulation
  37. .SH SYNOPSIS
  38. .B screen
  39. [
  40. .B \-a
  41. ] [
  42. .B \-f
  43. ] [
  44. .B \-n
  45. ] [
  46. .B \-e\fIxy\fP
  47. ] [
  48. .B \fIcmd args\fP ]
  49. .ta .5i 1.8i
  50. .SH DESCRIPTION
  51. .I screen
  52. is a full-screen window manager that
  53. multiplexes a physical terminal between several processes (typically
  54. interactive shells).  Each virtual terminal provides the functions
  55. of the DEC VT100 terminal and, in addition, several control functions
  56. from the ANSI X3.64 (ISO 6429) standard (e.g. insert/delete line).
  57. .PP
  58. When
  59. .I screen
  60. is called, it creates a single window with a shell; the pathname of the
  61. shell is taken from the environment symbol $SHELL; if this is not
  62. defined, \*Q/bin/sh\*U is used.
  63. New windows can be created at any time by calling
  64. .I screen
  65. from within a previously created window.
  66. The program to be started in a newly created
  67. window and optional arguments to the program can be supplied when
  68. .I screen
  69. is invoked.
  70. For instance,
  71. .IP
  72. screen csh
  73. .PP
  74. will create a window with a C-Shell and switch to that window.
  75. When the process associated with the currently displayed window
  76. terminates (e.g. ^D has been typed to a shell),
  77. .I screen
  78. switches to the previously displayed window;
  79. when no more windows are left,
  80. .I screen
  81. exits.
  82. .PP
  83. When \*Q/etc/utmp\*U is writable by
  84. .IR screen ,
  85. an appropriate record is written to this file for each window and
  86. removed when the window is terminated.
  87. .SH "COMMAND KEYS"
  88. The standard way to create a new window is to type \*QC-a c\*U (the notation
  89. \*QC-x\*U will be used as a shorthand for Control-x in this manual; x is
  90. an arbitrary letter).
  91. \*QC-a c\*U creates a new window running a shell and switches to that
  92. window immediately, regardless of the state of the process running
  93. in the current window.
  94. .I Screen
  95. recognizes several such commands; each command consists of
  96. \*QC-a\*U followed by a one-letter function.
  97. For convenience, the letter after a \*QC-a\*U can be entered both with or
  98. without the control key pressed (with the exception of
  99. \*QC-a C-a\*U and \*QC-a a\*U; see below), thus, \*QC-a c\*U as well as
  100. \*QC-a C-c\*U can be used to create a window.
  101. .PP
  102. The following commands are recognized by
  103. .IR screen :
  104. .IP "\fBC-a c\fP or \fBC-a C-c\fP"
  105. Create a new window with a shell and switch to that window.
  106. .IP "\fBC-a k\fP or \fBC-a C-k\fP"
  107. Kill the current window and switch to the previously displayed window.
  108. .IP "\fBC-a C-\e\fP"
  109. Kill all windows and terminate
  110. .IR screen .
  111. .IP "\fBC-a C-a\fP\0\0\0\0\0"
  112. Switch to the previously displayed window.
  113. .IP "\fBC-a 0\fP to \fBC-a 9\fP"
  114. Switch to the window with the number 0 (1, 2, .., 9, respectively).
  115. When a new window is established, the first available number from the
  116. range 0..9 is assigned to this window.
  117. Thus, the first window can be activated by \*QC-a 0\*U; at most
  118. 10 windows can be present at any time.
  119. .IP "\fBC-a space\fP or \fBC-a C-space\fP or \fBC-a n\fP or \fBC-a C-n\fP"
  120. Switch to the next window.  This function can be used repeatedly to
  121. cycle through the list of windows.
  122. (Control-space is not supported by all terminals.)
  123. .IP "\fBC-a p\fP or \fBC-a C-p\fP or \fBC-a -\fP"
  124. Switch to the previous window (the opposite of \fBC-a space\fP).
  125. .IP "\fBC-a l\fP or \fBC-a C-l\fP"
  126. Redisplay the current window.
  127. .IP "\fBC-a z\fP or \fBC-a C-z\fP"
  128. Suspend
  129. .IR screen .
  130. .IP "\fBC-a h\fP or \fBC-a C-h\fP"
  131. Write a hardcopy of the current window to the file \*Qhardcopy.\fIn\fP\*U
  132. in the window's current directory,
  133. where \fIn\fP is the number of the current window.
  134. .IP "\fBC-a .\fP (Control-a dot)"
  135. Write the termcap entry for the virtual terminal of the currently active
  136. window to the file \*Q.termcap\*U in the directory \*Q$HOME/.screen\*U.
  137. This termcap entry is identical to the value of the environment symbol
  138. TERMCAP that is set up by
  139. .I screen
  140. for each window.
  141. .IP "\fBC-a w\fP or \fBC-a C-w\fP"
  142. Display a list of all windows.
  143. For each window, the number of the window and the process that has been
  144. started in the window is displayed; the current window is marked with a
  145. `*'.
  146. .IP "\fBC-a t\fP or \fBC-a C-t\fP"
  147. Print in the message line the time of day, the host name, the load averages
  148. over 1, 5, and 15 minutes (if this is available on your system),
  149. the cursor position of the current window in the form \*Q(colum,row)\*U
  150. starting with \*U(0,0)\*U, and an indication if flow control
  151. and (for the current window)
  152. insert mode, origin mode, wrap mode, and keypad application
  153. mode are enabled or not (indicated by a '+' or '-').
  154. .IP "\fBC-a v\fP or \fBC-a C-v\fP"
  155. Display the version.
  156. .IP "\fBC-a a\fP\0\0\0\0\0"
  157. Send the character \*QC-a\*U to the processes running in the window.
  158. .IP "\fBC-a s\fP or \fBC-a C-s\fP"
  159. Send a Control-s to the program running in the window.
  160. .IP "\fBC-a q\fP or \fBC-a C-q\fP"
  161. Send a Control-q to the program running in the window.
  162. .IP
  163. .PP
  164. The
  165. .B -e
  166. option can be used to specify a different command character and
  167. a character which, when typed immediately after the command character,
  168. generates a literal command character.
  169. The defaults for these two characters are \*QC-a\*U and `a'.
  170. (Note that the function to switch to the previous window is actually the
  171. command character typed twice; for instance, when
  172. .I screen
  173. is called with the option \*Q\fB-e]x\fP\*U (or \*Q\fB-e ]x\fP\*U),
  174. this function becomes \*Q]]\*U).
  175. .SH CUSTOMIZATION
  176. When
  177. .I screen
  178. is invoked, it executes initialization commands from the file
  179. \*Q.screenrc\*U in the user's home directory.
  180. Commands in \*Q.screenrc\*U are mainly used to automatically
  181. establish a number of windows each time
  182. .I screen
  183. is called, and to bind functions to specific keys.
  184. Each line in \*Q.screenrc\*U contains one initialization command; lines
  185. starting with `#' are ignored.
  186. Commands can have arguments; arguments are separated by tabs and spaces
  187. and can be surrounded by single quotes or double quotes.
  188. .PP
  189. The following initialization commands are recognized by
  190. .IR screen :
  191. .PP
  192. .ne 3
  193. .B "escape \fIxy\fP"
  194. .PP
  195. Set the command character to \fIx\fP and the character generating a literal
  196. command character to \fIy\fP (see the -e option above).
  197. .PP
  198. .ne 3
  199. .B "bell \fImessage\fP"
  200. .PP
  201. When a bell character is sent to a background window,
  202. .I screen
  203. displays a notification in the message line (see below).
  204. The notification message can be re-defined by means of the \*Qbell\*U
  205. command; each occurrence of `%' in \fImessage\fP is replaced by
  206. the number of the window to which a bell has been sent.
  207. The default message is
  208. .PP
  209.     Bell in window %
  210. .PP
  211. An empty message can be supplied to the \*Qbell\*U command to suppress
  212. output of a message line (bell "").
  213. .PP
  214. .ne 3
  215. .B "screen [\fIn\fP] [\fIcmds args\fP]"
  216. .PP
  217. Establish a window.
  218. If an optional number \fIn\fP in the range 0..9 is given, the window
  219. number \fIn\fP is assigned to the newly created window (or, if this
  220. number is already in use, the next higher number).
  221. Note that \fIn\fP has a value of zero for the standard shell window
  222. created after \*Q.screenrc\*U has been read.
  223. If a command is specified after \*Qscreen\*U, this command (with the given
  224. arguments) is started in the window; if no command is given, a shell
  225. is created in the window.
  226. Thus, if your \*Q.screenrc\*U contains the lines
  227. .PP
  228. .nf
  229.     # example for .screenrc:
  230.     screen 1
  231.     screen 2 telnet foobar
  232. .fi
  233. .PP
  234. .I screen
  235. creates a shell window (window #1), a window with a TELNET connection
  236. to the machine foobar (window #2), and, finally, a second shell window
  237. (the default window) which gets a window number of zero.
  238. When the initialization is completed,
  239. .I screen
  240. always switches to the default window, so window #0 is displayed
  241. when the above \*Q.screenrc\*U is used.
  242. .PP
  243. .ne 3
  244. .B "chdir [\fIdirectory\fP]"
  245. .PP
  246. Change the \fIcurrent directory\fP of
  247. .I screen
  248. to the specified directory or, if called without an argument,
  249. to the home directory (the value of the environment symbol $HOME).
  250. All windows that are created by means of the \*Qscreen\*U command
  251. from within \*Q.screenrc\*U or by means of \*QC-a c'' are running
  252. in the \fIcurrent directory\fP; the \fIcurrent directory\fP is
  253. initially the directory from which the shell command
  254. .I screen
  255. has been invoked.
  256. Hardcopy files are always written to the directory in which the current
  257. window has been created (that is, \fInot\fP in the current directory
  258. of the shell running in the window).
  259. .PP
  260. .ne 3
  261. .B "bind \fIkey\fP [\fIfunction\fP | \fIcmd args\fP]"
  262. .PP
  263. Bind a function to a key.
  264. By default, each function provided by
  265. .I screen
  266. is bound to one or more keys as indicated by the above table, e.g. the
  267. function to create a new window is bound to \*QC-c\*U and \*Qc\*U.
  268. The \*Qbind\*U command can be used to redefine the key bindings and to
  269. define new bindings.
  270. The \fIkey\fP
  271. argument is either a single character, a sequence of the form
  272. \*Q^x\*U meaning \*QC-x\*U, or an octal number specifying the
  273. ASCII code of the character.
  274. If no further argument is given, any previously established binding
  275. for this key is removed.
  276. The \fIfunction\fP argument can be one of the following keywords:
  277. .PP
  278. .nf
  279.     shell    Create new window with a shell
  280.     kill    Kill the current window
  281.     quit    Kill all windows and terminate
  282.     other    Switch to previously displayed window
  283.     next    Switch to the next window
  284.     prev    Switch to the previous window
  285.     redisplay    Redisplay current window
  286.     hardcopy    Make hardcopy of current window
  287.     termcap    Write termcap entry to $HOME/.screen/.termcap
  288.     suspend    Suspend \fIscreen\fP
  289.     windows    Display list of window
  290.     info    Print useful information in the message line
  291.     xon    Send Control-q
  292.     xoff    Send Control-s
  293.     version    Display the version
  294.     select0    Switch to window #0
  295.     \0\0...
  296.     select9    Switch to window #9
  297. .fi
  298. .PP
  299. In addition, a key can be bound such that a window is created running
  300. a different command than the shell when that key is pressed.
  301. In this case, the command optionally followed by
  302. arguments must be given instead of one of the above-listed keywords.
  303. For example, the commands
  304. .PP
  305. .nf
  306.     bind ' ' windows
  307.     bind ^f telnet foobar
  308.     bind 033 su
  309. .fi
  310. .PP
  311. would bind the space key to the function that displays a list
  312. of windows (that is, the function usually invoked by \*QC-a C-w\*U
  313. or \*QC-a w\*U would also be available as \*QC-a space\*U),
  314. bind \*QC-f\*U to the function \*Qcreate a window with a TELNET
  315. connection to foobar\*U, and bind \*Qescape\*U to the function
  316. that creates a window with a super-user shell.
  317. .SH "VIRTUAL TERMINAL"
  318. .I Screen
  319. prints error messages and other diagnostics in a \fImessage line\fP above
  320. the bottom of the screen.
  321. The message line is removed when a key is pressed or, automatically,
  322. after a couple of seconds.
  323. The message line facility can be used by an application running in
  324. the current window by means of the ANSI \fIPrivacy message\fP
  325. control sequence (for instance, from within the shell, something like
  326. .IP
  327. echo '^[^Hello world^[\e'   (where ^[ is an \fIescape\fP)
  328. .PP
  329. can be used to display a message line.
  330. .PP
  331. When the `NF' capability is found in the termcap entry of the
  332. terminal on which
  333. .I screen
  334. has been started, flow control is turned off for the terminal.
  335. This enables the user to send XON and XOFF characters to the
  336. program running in a window (this is required by the \fIemacs\fP
  337. editor, for instance).
  338. The command line options 
  339. .B -n
  340. and
  341. .B -f
  342. can be used to turn flow control off or on, respectively, independently
  343. of the `NF' capability.
  344. .PP
  345. .I
  346. Screen
  347. never writes in the last position of the screen, unless the boolean
  348. capability `LP' is found in the termcap entry of the terminal.
  349. Usually,
  350. .I screen
  351. cannot predict whether or not a particular terminal scrolls when
  352. a character is written in the last column of the last line;
  353. `LP' indicates that it is safe to write in this position.
  354. Note that the `LP' capability is independent of `am' (automatic
  355. margins); for certain terminals, such as the VT100, it is reasonable
  356. to set `am' as well as `LP' in the corresponding termcap entry
  357. (the VT100 does not move the cursor when a character is written in
  358. the last column of each line).
  359. .PP
  360. .I Screen
  361. puts into the environment of each process started in a newly created
  362. window the symbols \*QWINDOW=\fIn\fP\*U (where \fIn\fP is the number
  363. of the respective window), \*QTERM=screen\*U, and a TERMCAP variable
  364. reflecting the capabilities of the virtual terminal emulated by
  365. .IR screen .
  366. The actual set of capabilities supported by the virtual terminal
  367. depends on the capabilities supported by the physical terminal.
  368. If, for instance, the physical terminal does not support standout mode,
  369. .I screen
  370. does not put the `so' and `se' capabilities into the window's TERMCAP
  371. variable, accordingly. 
  372. However, a minimum number of capabilities must be supported by a
  373. terminal in order to run
  374. .IR screen ,
  375. namely scrolling, clear screen, and direct cursor addressing
  376. (in addition,
  377. .I screen
  378. does not run on hardcopy terminals or on terminals that overstrike).
  379. .PP
  380. Some capabilities are only put into the TERMCAP
  381. variable of the virtual terminal if they can be efficiently
  382. implemented by the physical terminal.
  383. For instance, `dl' (delete line) is only put into the TERMCAP
  384. variable if the terminal supports either delete line itself or
  385. scrolling regions.
  386. If
  387. .I screen
  388. is called with the
  389. .B -a
  390. option, \fIall\fP capabilities are put into the environment,
  391. even if
  392. .I screen
  393. must redraw parts of the display in order to implement a function.
  394. .PP
  395. The following is a list of control sequences recognized by
  396. .IR screen .
  397. \*Q(V)\*U and \*Q(A)\*U indicate VT100-specific and ANSI-specific
  398. functions, respectively.
  399. .PP
  400. .nf
  401. .TP 20
  402. .B "ESC E"
  403.     Next Line
  404. .TP 20
  405. .B "ESC D"
  406.     Index
  407. .TP 20
  408. .B "ESC M"
  409.     Reverse Index
  410. .TP 20
  411. .B "ESC H"
  412.     Horizontal Tab Set
  413. .TP 20
  414. .B "ESC 7"
  415. (V)    Save Cursor and attributes
  416. .TP 20
  417. .B "ESC 8"
  418. (V)    Restore Cursor and Attributes
  419. .TP 20
  420. .B "ESC c"
  421.     Reset to Initial State
  422. .TP 20
  423. .B "ESC ="
  424. (V)    Application Keypad Mode
  425. .TP 20
  426. .B "ESC >"
  427. (V)    Numeric Keypad Mode
  428. .TP 20
  429. .B "ESC # 8"
  430. (V)    Fill Screen with E's
  431. .TP 20
  432. .B "ESC \e"
  433. (A)    String Terminator
  434. .TP 20
  435. .B "ESC ^"
  436. (A)    Privacy Message (Message Line)
  437. .TP 20
  438. .B "ESC P"
  439. (A)    Device Control String (not used)
  440. .TP 20
  441. .B "ESC _"
  442. (A)    Application Program Command (not used)
  443. .TP 20
  444. .B "ESC ]"
  445. (A)    Operating System Command (not used)
  446. .TP 20
  447. .B "ESC [ Pn ; Pn H"
  448.     Direct Cursor Addressing
  449. .TP 20
  450. .B "ESC [ Pn ; Pn f"
  451.     Direct Cursor Addressing
  452. .TP 20
  453. .B "ESC [ Pn J"
  454.     Erase in Display
  455. .TP 20
  456. \h'\w'ESC 'u'Pn = None or \fB0\fP
  457.     From Cursor to End of Screen
  458. .TP 20
  459. \h'\w'ESC 'u'\fB1\fP
  460.     From Beginning of Screen to Cursor
  461. .TP 20
  462. \h'\w'ESC 'u'\fB2\fP
  463.     Entire Screen
  464. .TP 20
  465. .B "ESC [ Pn K"
  466.     Erase in Line
  467. .TP 20
  468. \h'\w'ESC 'u'Pn = None or \fB0\fP
  469.     From Cursor to End of Line
  470. .TP 20
  471. \h'\w'ESC 'u'\fB1\fP
  472.     From Beginning of Line to Cursor
  473. .TP 20
  474. \h'\w'ESC 'u'\fB2\fP
  475.     Entire Line
  476. .TP 20
  477. .B "ESC [ Pn A"
  478.     Cursor Up
  479. .TP 20
  480. .B "ESC [ Pn B"
  481.     Cursor Down
  482. .TP 20
  483. .B "ESC [ Pn C"
  484.     Cursor Right
  485. .TP 20
  486. .B "ESC [ Pn D"
  487.     Cursor Left
  488. .TP 20
  489. .B "ESC [ Ps ;...; Ps m"
  490.     Select Graphic Rendition
  491. .TP 20
  492. \h'\w'ESC 'u'Ps = None or \fB0\fP
  493.     Default Rendition
  494. .TP 20
  495. \h'\w'ESC 'u'\fB1\fP
  496.     Bold
  497. .TP 20
  498. \h'\w'ESC 'u'\fB2\fP
  499. (A)    Faint
  500. .TP 20
  501. \h'\w'ESC 'u'\fB3\fP
  502. (A)    \fIStandout\fP Mode (ANSI: Italicised)
  503. .TP 20
  504. \h'\w'ESC 'u'\fB4\fP
  505.     Underlined
  506. .TP 20
  507. \h'\w'ESC 'u'\fB5\fP
  508.     Blinking
  509. .TP 20
  510. \h'\w'ESC 'u'\fB7\fP
  511.     Negative Image
  512. .TP 20
  513. \h'\w'ESC 'u'\fB22\fP
  514. (A)    Normal Intensity
  515. .TP 20
  516. \h'\w'ESC 'u'\fB23\fP
  517. (A)    \fIStandout\fP Mode off (ANSI: Italicised off)
  518. .TP 20
  519. \h'\w'ESC 'u'\fB24\fP
  520. (A)    Not Underlined
  521. .TP 20
  522. \h'\w'ESC 'u'\fB25\fP
  523. (A)    Not Blinking
  524. .TP 20
  525. \h'\w'ESC 'u'\fB27\fP
  526. (A)    Positive Image
  527. .TP 20
  528. .B "ESC [ Pn g"
  529.     Tab Clear
  530. .TP 20
  531. \h'\w'ESC 'u'Pn = None or \fB0\fP
  532.     Clear Tab at Current Position
  533. .TP 20
  534. \h'\w'ESC 'u'\fB3\fP
  535.     Clear All Tabs
  536. .TP 20
  537. .B "ESC [ Pn ; Pn r"
  538. (V)    Set Scrolling Region
  539. .TP 20
  540. .B "ESC [ Pn I"
  541. (A)    Horizontal Tab
  542. .TP 20
  543. .B "ESC [ Pn Z"
  544. (A)    Backward Tab
  545. .TP 20
  546. .B "ESC [ Pn L"
  547. (A)    Insert Line
  548. .TP 20
  549. .B "ESC [ Pn M"
  550. (A)    Delete Line
  551. .TP 20
  552. .B "ESC [ Pn @"
  553. (A)    Insert Character
  554. .TP 20
  555. .B "ESC [ Pn P"
  556. (A)    Delete Character
  557. .TP 20
  558. .B "ESC [ Ps  ;...; Ps h"
  559.     Set Mode
  560. .TP 20
  561. .B "ESC [ Ps  ;...; Ps l"
  562.     Reset Mode
  563. .TP 20
  564. \h'\w'ESC 'u'Ps = \fB4\fP
  565. (A)    Insert Mode
  566. .TP 20
  567. \h'\w'ESC 'u'\fB?5\fP
  568. (V)    Visible Bell (\fIOn\fP followed by \fIOff\fP)
  569. .TP 20
  570. \h'\w'ESC 'u'\fB?6\fP
  571. (V)    \fIOrigin\fP Mode
  572. .TP 20
  573. \h'\w'ESC 'u'\fB?7\fP
  574. (V)    \fIWrap\fP Mode
  575. .fi
  576. .SH FILES
  577. .nf
  578. .ta 2i
  579. $(HOME)/.screenrc    \fIscreen\fP initialization commands
  580. .br
  581. $(HOME)/.screen    Directory created by \fIscreen\fP
  582. .br
  583. $(HOME)/.screen/\fItty\fP    Socket created by \fIscreen\fP
  584. .br
  585. hardcopy.[0-9]    Screen images created by the hardcopy function
  586. .br
  587. /etc/termcap    Terminal capability data base
  588. .br
  589. /etc/utmp    Login records
  590. .br
  591. /etc/ttys    Terminal initialization data
  592. .fi
  593. .SH "SEE ALSO"
  594. termcap(5), utmp(5)
  595. .SH AUTHOR
  596. Oliver Laumann
  597. .SH BUGS
  598. Standout mode is not cleared before newline or cursor addressing.
  599. .PP
  600. If `LP' is not set but `am' is set, the last character in the last line is never
  601. written, and it is not correctly re-displayed when the screen is
  602. scrolled up or when a character is deleted in the last line.
  603. .PP
  604. The VT100 \*Qwrap around with cursor addressing\*U bug is not compensated
  605. when
  606. .I screen
  607. is running on a VT100.
  608. .PP
  609. `AL,' `DL', and similar parameterized capabilities are not used if present.
  610. .PP
  611. `dm' (delete mode), `xn', and `xs' are not handled
  612. correctly (they are ignored). 
  613. .PP
  614. Different character sets are not supported.
  615. .PP
  616. `ms' is not advertised in the termcap entry (in order to compensate
  617. a bug in
  618. .IR curses (3X)).
  619. .PP
  620. Scrolling regions are only emulated if the physical terminal supports
  621. scrolling regions.
  622. .PP
  623. .I Screen
  624. does not make use of hardware tabs.
  625. .PP
  626. .I Screen
  627. must be installed as set-uid with owner root in order to be able
  628. to correctly change the owner of the tty device file for each
  629. window.
  630. Special permission may also be required to write the file \*Q/etc/utmp\*U.
  631. .PP
  632. Entries in \*Q/etc/utmp\*U are not removed when
  633. .I screen
  634. is killed with SIGKILL.
  635. SHAR_EOF
  636. if test 17895 -ne "`wc -c < 'screen.1'`"
  637. then
  638.     echo shar: error transmitting "'screen.1'" '(should have been 17895 characters)'
  639. fi
  640. fi # end of overwriting check
  641. echo shar: extracting "'screen.c'" '(32087 characters)'
  642. if test -f 'screen.c'
  643. then
  644.     echo shar: will not over-write existing file "'screen.c'"
  645. else
  646. cat << \SHAR_EOF > 'screen.c'
  647. /* Copyright (c) 1987, Oliver Laumann, Technical University of Berlin.
  648.  * Not derived from licensed software.
  649.  *
  650.  * Permission is granted to freely use, copy, modify, and redistribute
  651.  * this software, provided that no attempt is made to gain profit from it,
  652.  * the author is not construed to be liable for any results of using the
  653.  * software, alterations are clearly marked as such, and this notice is
  654.  * not modified.
  655.  */
  656.  
  657. static char ScreenVersion[] = "screen 1.1i  1-Jul-87";
  658.  
  659. #include <stdio.h>
  660. #include <sgtty.h>
  661. #include <signal.h>
  662. #include <errno.h>
  663. #include <ctype.h>
  664. #include <utmp.h>
  665. #include <pwd.h>
  666. #include <nlist.h>
  667. #include <sys/types.h>
  668. #include <sys/time.h>
  669. #include <sys/wait.h>
  670. #include <sys/socket.h>
  671. #include <sys/un.h>
  672. #include <sys/stat.h>
  673. #include <sys/file.h>
  674. #include "screen.h"
  675.  
  676. #ifdef GETTTYENT
  677. #   include <ttyent.h>
  678. #else
  679.     static struct ttyent {
  680.     char *ty_name;
  681.     } *getttyent();
  682.     static char *tt, *ttnext;
  683.     static char ttys[] = "/etc/ttys";
  684. #endif
  685.  
  686. #define MAXWIN     10
  687. #define MAXARGS    64
  688. #define MAXLINE  1024
  689. #define MSGWAIT     5
  690.  
  691. #define Ctrl(c) ((c)&037)
  692.  
  693. extern char *blank, Term[], **environ;
  694. extern rows, cols;
  695. extern status;
  696. extern time_t TimeDisplayed;
  697. extern char AnsiVersion[];
  698. extern short ospeed;
  699. extern flowctl;
  700. extern errno;
  701. extern sys_nerr;
  702. extern char *sys_errlist[];
  703. extern char *index(), *rindex(), *malloc(), *getenv(), *MakeTermcap();
  704. extern char *getlogin(), *ttyname();
  705. static Finit(), SigChld();
  706. static char *MakeBellMsg(), *Filename(), **SaveArgs();
  707.  
  708. static char PtyName[32], TtyName[32];
  709. static char *ShellProg;
  710. static char *ShellArgs[2];
  711. static char inbuf[IOSIZE];
  712. static inlen;
  713. static ESCseen;
  714. static GotSignal;
  715. static char DefaultShell[] = "/bin/sh";
  716. static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
  717. static char PtyProto[] = "/dev/ptyXY";
  718. static char TtyProto[] = "/dev/ttyXY";
  719. static char SockPath[512];
  720. static char SockDir[] = ".screen";
  721. static char *SockName;
  722. static char *NewEnv[MAXARGS];
  723. static char Esc = Ctrl('a');
  724. static char MetaEsc = 'a';
  725. static char *home;
  726. static HasWindow;
  727. static utmp, utmpf;
  728. static char UtmpName[] = "/etc/utmp";
  729. static char *LoginName;
  730. static char *BellString = "Bell in window %";
  731. static mflag, nflag, fflag;
  732. static char HostName[MAXSTR];
  733. #ifdef LOADAV
  734.     static char KmemName[] = "/dev/kmem";
  735.     static char UnixName[] = "/vmunix";
  736.     static char AvenrunSym[] = "_avenrun";
  737.     static struct nlist nl[2];
  738.     static avenrun, kmemf;
  739. #endif
  740.  
  741. struct mode {
  742.     struct sgttyb m_ttyb;
  743.     struct tchars m_tchars;
  744.     struct ltchars m_ltchars;
  745.     int m_ldisc;
  746.     int m_lmode;
  747. } OldMode, NewMode;
  748.  
  749. static struct win *curr, *other;
  750. static CurrNum, OtherNum;
  751. static struct win *wtab[MAXWIN];
  752.  
  753. #define MSG_CREATE    0
  754. #define MSG_ERROR     1
  755.  
  756. struct msg {
  757.     int type;
  758.     union {
  759.     struct {
  760.         int aflag;
  761.         int nargs;
  762.         char line[MAXLINE];
  763.         char dir[1024];
  764.     } create;
  765.     char message[MAXLINE];
  766.     } m;
  767. };
  768.  
  769. #define KEY_IGNORE         0
  770. #define KEY_HARDCOPY       1
  771. #define KEY_SUSPEND        2
  772. #define KEY_SHELL          3
  773. #define KEY_NEXT           4
  774. #define KEY_PREV           5
  775. #define KEY_KILL           6
  776. #define KEY_REDISPLAY      7
  777. #define KEY_WINDOWS        8
  778. #define KEY_VERSION        9
  779. #define KEY_OTHER         10
  780. #define KEY_0             11
  781. #define KEY_1             12
  782. #define KEY_2             13
  783. #define KEY_3             14
  784. #define KEY_4             15
  785. #define KEY_5             16
  786. #define KEY_6             17
  787. #define KEY_7             18
  788. #define KEY_8             19
  789. #define KEY_9             20
  790. #define KEY_XON           21
  791. #define KEY_XOFF          22
  792. #define KEY_INFO          23
  793. #define KEY_TERMCAP       24
  794. #define KEY_QUIT          25
  795. #define KEY_CREATE        26
  796.  
  797. struct key {
  798.     int type;
  799.     char **args;
  800. } ktab[256];
  801.  
  802. char *KeyNames[] = {
  803.     "hardcopy", "suspend", "shell", "next", "prev", "kill", "redisplay",
  804.     "windows", "version", "other", "select0", "select1", "select2", "select3",
  805.     "select4", "select5", "select6", "select7", "select8", "select9",
  806.     "xon", "xoff", "info", "termcap", "quit",
  807.     0
  808. };
  809.  
  810. main (ac, av) char **av; {
  811.     register n, len;
  812.     register struct win **pp, *p;
  813.     char *ap;
  814.     int s, r, w, x = 0;
  815.     int aflag = 0;
  816.     struct timeval tv;
  817.     time_t now;
  818.     char buf[IOSIZE], *myname = (ac == 0) ? "screen" : av[0];
  819.     char rc[256];
  820.  
  821.     while (ac > 0) {
  822.     ap = *++av;
  823.     if (--ac > 0 && *ap == '-') {
  824.         switch (ap[1]) {
  825.         case 'c':        /* Compatibility with older versions. */
  826.         break;
  827.         case 'a':
  828.         aflag = 1;
  829.         break;
  830.         case 'm':
  831.         mflag = 1;
  832.         break;
  833.         case 'n':
  834.         nflag = 1;
  835.         break;
  836.         case 'f':
  837.         fflag = 1;
  838.         break;
  839.         case 'e':
  840.         if (ap[2]) {
  841.             ap += 2;
  842.         } else {
  843.             if (--ac == 0) goto help;
  844.             ap = *++av;
  845.         }
  846.         if (strlen (ap) != 2)
  847.             Msg (0, "Two characters are required with -e option.");
  848.         Esc = ap[0];
  849.         MetaEsc = ap[1];
  850.         break;
  851.         default:
  852.         help:
  853.         Msg (0, "Use: %s [-a] [-f] [-n] [-exy] [cmd args]", myname);
  854.         }
  855.     } else break;
  856.     }
  857.     if (nflag && fflag)
  858.     Msg (0, "-f and -n are conflicting options.");
  859.     if ((ShellProg = getenv ("SHELL")) == 0)
  860.     ShellProg = DefaultShell;
  861.     ShellArgs[0] = ShellProg;
  862.     if (ac == 0) {
  863.     ac = 1;
  864.     av = ShellArgs;
  865.     }
  866.     if (GetSockName ()) {
  867.     /* Client */
  868.     s = MakeClientSocket ();
  869.     SendCreateMsg (s, ac, av, aflag);
  870.     close (s);
  871.     exit (0);
  872.     }
  873.     (void) gethostname (HostName, MAXSTR);
  874.     HostName[MAXSTR-1] = '\0';
  875.     if (ap = index (HostName, '.'))
  876.     *ap = '\0';
  877.     s = MakeServerSocket ();
  878.     InitTerm ();
  879.     if (fflag)
  880.     flowctl = 1;
  881.     else if (nflag)
  882.     flowctl = 0;
  883.     MakeNewEnv ();
  884.     GetTTY (0, &OldMode);
  885.     ospeed = (short)OldMode.m_ttyb.sg_ospeed;
  886.     InitUtmp ();
  887. #ifdef LOADAV
  888.     InitKmem ();
  889. #endif
  890.     signal (SIGHUP, Finit);
  891.     signal (SIGINT, Finit);
  892.     signal (SIGQUIT, Finit);
  893.     signal (SIGTERM, Finit);
  894.     InitKeytab ();
  895.     sprintf (rc, "%.*s/.screenrc", 245, home);
  896.     ReadRc (rc);
  897.     if ((n = MakeWindow (*av, av, aflag, 0, (char *)0)) == -1) {
  898.     SetTTY (0, &OldMode);
  899.     FinitTerm ();
  900.     exit (1);
  901.     }
  902.     SetCurrWindow (n);
  903.     HasWindow = 1;
  904.     SetMode (&OldMode, &NewMode);
  905.     SetTTY (0, &NewMode);
  906.     signal (SIGCHLD, SigChld);
  907.     tv.tv_usec = 0;
  908.     while (1) {
  909.     if (status) {
  910.         time (&now);
  911.         if (now - TimeDisplayed < MSGWAIT) {
  912.         tv.tv_sec = MSGWAIT - (now - TimeDisplayed);
  913.         } else RemoveStatus (curr);
  914.     }
  915.     r = 0;
  916.     w = 0;
  917.     if (inlen)
  918.         w |= 1 << curr->ptyfd;
  919.     else
  920.         r |= 1 << 0;
  921.     for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  922.         if (!(p = *pp))
  923.         continue;
  924.         if ((*pp)->active && status)
  925.         continue;
  926.         if ((*pp)->outlen > 0)
  927.         continue;
  928.         r |= 1 << (*pp)->ptyfd;
  929.     }
  930.     r |= 1 << s;
  931.     fflush (stdout);
  932.     if (select (32, &r, &w, &x, status ? &tv : (struct timeval *)0) == -1) {
  933.         if (errno == EINTR)
  934.         continue;
  935.         HasWindow = 0;
  936.         Msg (errno, "select");
  937.         /*NOTREACHED*/
  938.     }
  939.     if (GotSignal && !status) {
  940.         SigHandler ();
  941.         continue;
  942.     }
  943.     if (r & 1 << s) {
  944.         RemoveStatus (curr);
  945.         ReceiveMsg (s);
  946.     }
  947.     if (r & 1 << 0) {
  948.         RemoveStatus (curr);
  949.         if (ESCseen) {
  950.         inbuf[0] = Esc;
  951.         inlen = read (0, inbuf+1, IOSIZE-1) + 1;
  952.         ESCseen = 0;
  953.         } else {
  954.         inlen = read (0, inbuf, IOSIZE);
  955.         }
  956.         if (inlen > 0)
  957.         inlen = ProcessInput (inbuf, inlen);
  958.         if (inlen > 0)
  959.         continue;
  960.     }
  961.     if (GotSignal && !status) {
  962.         SigHandler ();
  963.         continue;
  964.     }
  965.     if (w & 1 << curr->ptyfd && inlen > 0) {
  966.         if ((len = write (curr->ptyfd, inbuf, inlen)) > 0) {
  967.         inlen -= len;
  968.         bcopy (inbuf+len, inbuf, inlen);
  969.         }
  970.     }
  971.     if (GotSignal && !status) {
  972.         SigHandler ();
  973.         continue;
  974.     }
  975.     for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  976.         if (!(p = *pp))
  977.         continue;
  978.         if (p->outlen) {
  979.         WriteString (p, p->outbuf, p->outlen);
  980.         } else if (r & 1 << p->ptyfd) {
  981.         if ((len = read (p->ptyfd, buf, IOSIZE)) == -1) {
  982.             if (errno == EWOULDBLOCK)
  983.             len = 0;
  984.         }
  985.         if (len > 0)
  986.             WriteString (p, buf, len);
  987.         }
  988.         if (p->bell) {
  989.         p->bell = 0;
  990.         Msg (0, MakeBellMsg (pp-wtab));
  991.         }
  992.     }
  993.     if (GotSignal && !status)
  994.         SigHandler ();
  995.     }
  996.     /*NOTREACHED*/
  997. }
  998.  
  999. static SigHandler () {
  1000.     while (GotSignal) {
  1001.     GotSignal = 0;
  1002.     DoWait ();
  1003.     }
  1004. }
  1005.  
  1006. static SigChld () {
  1007.     GotSignal = 1;
  1008. }
  1009.  
  1010. static DoWait () {
  1011.     register pid;
  1012.     register struct win **pp;
  1013.     union wait wstat;
  1014.  
  1015.     while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0) {
  1016.     for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  1017.         if (*pp && pid == (*pp)->wpid) {
  1018.         if (WIFSTOPPED (wstat)) {
  1019.             kill((*pp)->wpid, SIGCONT);
  1020.         } else {
  1021.             if (*pp == curr)
  1022.             curr = 0;
  1023.             if (*pp == other)
  1024.             other = 0;
  1025.             FreeWindow (*pp);
  1026.             *pp = 0;
  1027.         }
  1028.         }
  1029.     }
  1030.     }
  1031.     CheckWindows ();
  1032. }
  1033.  
  1034. static CheckWindows () {
  1035.     register struct win **pp;
  1036.  
  1037.     /* If the current window disappeared and the "other" window is still
  1038.      * there, switch to the "other" window, else switch to the window
  1039.      * with the lowest index.
  1040.      * If there current window is still there, but the "other" window
  1041.      * vanished, "SetCurrWindow" is called in order to assign a new value
  1042.      * to "other".
  1043.      * If no window is alive at all, exit.
  1044.      */
  1045.     if (!curr && other) {
  1046.     SwitchWindow (OtherNum);
  1047.     return;
  1048.     }
  1049.     if (curr && !other) {
  1050.     SetCurrWindow (CurrNum);
  1051.     return;
  1052.     }
  1053.     for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  1054.     if (*pp) {
  1055.         if (!curr)
  1056.         SwitchWindow (pp-wtab);
  1057.         return;
  1058.     }
  1059.     }
  1060.     Finit ();
  1061. }
  1062.  
  1063. static Finit () {
  1064.     register struct win *p, **pp;
  1065.  
  1066.     for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  1067.     if (p = *pp)
  1068.         FreeWindow (p);
  1069.     }
  1070.     SetTTY (0, &OldMode);
  1071.     FinitTerm ();
  1072.     printf ("[screen is terminating]\n");
  1073.     exit (0);
  1074. }
  1075.  
  1076. static InitKeytab () {
  1077.     register i;
  1078.  
  1079.     ktab['h'].type = ktab[Ctrl('h')].type = KEY_HARDCOPY;
  1080.     ktab['z'].type = ktab[Ctrl('z')].type = KEY_SUSPEND;
  1081.     ktab['c'].type = ktab[Ctrl('c')].type = KEY_SHELL;
  1082.     ktab[' '].type = ktab[Ctrl(' ')].type = 
  1083.     ktab['n'].type = ktab[Ctrl('n')].type = KEY_NEXT;
  1084.     ktab['-'].type = ktab['p'].type = ktab[Ctrl('p')].type = KEY_PREV;
  1085.     ktab['k'].type = ktab[Ctrl('k')].type = KEY_KILL;
  1086.     ktab['l'].type = ktab[Ctrl('l')].type = KEY_REDISPLAY;
  1087.     ktab['w'].type = ktab[Ctrl('w')].type = KEY_WINDOWS;
  1088.     ktab['v'].type = ktab[Ctrl('v')].type = KEY_VERSION;
  1089.     ktab['q'].type = ktab[Ctrl('q')].type = KEY_XON;
  1090.     ktab['s'].type = ktab[Ctrl('s')].type = KEY_XOFF;
  1091.     ktab['t'].type = ktab[Ctrl('t')].type = KEY_INFO;
  1092.     ktab['.'].type = KEY_TERMCAP;
  1093.     ktab[Ctrl('\\')].type = KEY_QUIT;
  1094.     ktab[Esc].type = KEY_OTHER;
  1095.     for (i = 0; i <= 9; i++)
  1096.     ktab[i+'0'].type = KEY_0+i;
  1097. }
  1098.  
  1099. static ProcessInput (buf, len) char *buf; {
  1100.     register n, k;
  1101.     register char *s, *p;
  1102.     register struct win **pp;
  1103.  
  1104.     for (s = p = buf; len > 0; len--, s++) {
  1105.     if (*s == Esc) {
  1106.         if (len > 1) {
  1107.         len--; s++;
  1108.         k = ktab[*s].type;
  1109.         if (*s == MetaEsc) {
  1110.             *p++ = Esc;
  1111.         } else if (k >= KEY_0 && k <= KEY_9) {
  1112.             p = buf;
  1113.             SwitchWindow (k - KEY_0);
  1114.         } else switch (ktab[*s].type) {
  1115.         case KEY_TERMCAP:
  1116.             p = buf;
  1117.             WriteFile (0);
  1118.             break;
  1119.         case KEY_HARDCOPY:
  1120.             p = buf;
  1121.             WriteFile (1);
  1122.             break;
  1123.         case KEY_SUSPEND:
  1124.             p = buf;
  1125.             SetTTY (0, &OldMode);
  1126.             FinitTerm ();
  1127.             kill (getpid (), SIGTSTP);
  1128.             SetTTY (0, &NewMode);
  1129.             Activate (wtab[CurrNum]);
  1130.             break;
  1131.         case KEY_SHELL:
  1132.             p = buf;
  1133.             if ((n = MakeWindow (ShellProg, ShellArgs,
  1134.                 0, 0, (char *)0)) != -1)
  1135.             SwitchWindow (n);
  1136.             break;
  1137.         case KEY_NEXT:
  1138.             p = buf;
  1139.             if (MoreWindows ())
  1140.             SwitchWindow (NextWindow ());
  1141.             break;
  1142.         case KEY_PREV:
  1143.             p = buf;
  1144.             if (MoreWindows ())
  1145.             SwitchWindow (PreviousWindow ());
  1146.             break;
  1147.         case KEY_KILL:
  1148.             p = buf;
  1149.             FreeWindow (wtab[CurrNum]);
  1150.             if (other == curr)
  1151.             other = 0;
  1152.             curr = wtab[CurrNum] = 0;
  1153.             CheckWindows ();
  1154.             break;
  1155.         case KEY_QUIT:
  1156.             for (pp = wtab; pp < wtab+MAXWIN; ++pp)
  1157.             if (*pp) FreeWindow (*pp);
  1158.             Finit ();
  1159.             /*NOTREACHED*/
  1160.         case KEY_REDISPLAY:
  1161.             p = buf;
  1162.             Activate (wtab[CurrNum]);
  1163.             break;
  1164.         case KEY_WINDOWS:
  1165.             p = buf;
  1166.             ShowWindows ();
  1167.             break;
  1168.         case KEY_VERSION:
  1169.             p = buf;
  1170.             Msg (0, "%s  %s", ScreenVersion, AnsiVersion);
  1171.             break;
  1172.         case KEY_INFO:
  1173.             p = buf;
  1174.             ShowInfo ();
  1175.             break;
  1176.         case KEY_OTHER:
  1177.             p = buf;
  1178.             if (MoreWindows ())
  1179.             SwitchWindow (OtherNum);
  1180.             break;
  1181.         case KEY_XON:
  1182.             *p++ = Ctrl('q');
  1183.             break;
  1184.         case KEY_XOFF:
  1185.             *p++ = Ctrl('s');
  1186.             break;
  1187.         case KEY_CREATE:
  1188.             p = buf;
  1189.             if ((n = MakeWindow (ktab[*s].args[0], ktab[*s].args,
  1190.                 0, 0, (char *)0)) != -1)
  1191.             SwitchWindow (n);
  1192.             break;
  1193.         }
  1194.         } else ESCseen = 1;
  1195.     } else *p++ = *s;
  1196.     }
  1197.     return p - buf;
  1198. }
  1199.  
  1200. static SwitchWindow (n) {
  1201.     if (!wtab[n])
  1202.     return;
  1203.     SetCurrWindow (n);
  1204.     Activate (wtab[n]);
  1205. }
  1206.  
  1207. static SetCurrWindow (n) {
  1208.     /*
  1209.      * If we come from another window, this window becomes the
  1210.      * "other" window:
  1211.      */
  1212.     if (curr) {
  1213.     curr->active = 0;
  1214.     other = curr;
  1215.     OtherNum = CurrNum;
  1216.     }
  1217.     CurrNum = n;
  1218.     curr = wtab[n];
  1219.     curr->active = 1;
  1220.     /*
  1221.      * If the "other" window is currently undefined (at program start
  1222.      * or because it has died), or if the "other" window is equal to the
  1223.      * one just selected, we try to find a new one:
  1224.      */
  1225.     if (other == 0 || other == curr) {
  1226.     OtherNum = NextWindow ();
  1227.     other = wtab[OtherNum];
  1228.     }
  1229. }
  1230.  
  1231. static NextWindow () {
  1232.     register struct win **pp;
  1233.  
  1234.     for (pp = wtab+CurrNum+1; pp != wtab+CurrNum; ++pp) {
  1235.     if (pp == wtab+MAXWIN)
  1236.         pp = wtab;
  1237.     if (*pp)
  1238.         break;
  1239.     }
  1240.     return pp-wtab;
  1241. }
  1242.  
  1243. static PreviousWindow () {
  1244.     register struct win **pp;
  1245.  
  1246.     for (pp = wtab+CurrNum-1; pp != wtab+CurrNum; --pp) {
  1247.     if (pp < wtab)
  1248.         pp = wtab+MAXWIN-1;
  1249.     if (*pp)
  1250.         break;
  1251.     }
  1252.     return pp-wtab;
  1253. }
  1254.  
  1255. static MoreWindows () {
  1256.     register struct win **pp;
  1257.     register n;
  1258.  
  1259.     for (n = 0, pp = wtab; pp < wtab+MAXWIN; ++pp)
  1260.     if (*pp) ++n;
  1261.     if (n <= 1)
  1262.     Msg (0, "No other window.");
  1263.     return n > 1;
  1264. }
  1265.  
  1266. static FreeWindow (wp) struct win *wp; {
  1267.     register i;
  1268.  
  1269.     RemoveUtmp (wp->slot);
  1270.     (void) chmod (wp->tty, 0666);
  1271.     (void) chown (wp->tty, 0, 0);
  1272.     close (wp->ptyfd);
  1273.     for (i = 0; i < rows; ++i) {
  1274.     free (wp->image[i]);
  1275.     free (wp->attr[i]);
  1276.     }
  1277.     free (wp->image);
  1278.     free (wp->attr);
  1279.     free (wp);
  1280. }
  1281.  
  1282. static MakeWindow (prog, args, aflag, StartAt, dir)
  1283.     char *prog, **args, *dir; {
  1284.     register struct win **pp, *p;
  1285.     register char **cp;
  1286.     register n, f;
  1287.     int tf;
  1288.     int mypid;
  1289.     char ebuf[10];
  1290.  
  1291.     pp = wtab+StartAt;
  1292.     do {
  1293.     if (*pp == 0)
  1294.         break;
  1295.     if (++pp == wtab+MAXWIN)
  1296.         pp = wtab;
  1297.     } while (pp != wtab+StartAt);
  1298.     if (*pp) {
  1299.     Msg (0, "No more windows.");
  1300.     return -1;
  1301.     }
  1302.     n = pp - wtab;
  1303.     if ((f = OpenPTY ()) == -1) {
  1304.     Msg (0, "No more PTYs.");
  1305.     return -1;
  1306.     }
  1307.     fcntl (f, F_SETFL, FNDELAY);
  1308.     if ((p = *pp = (struct win *)malloc (sizeof (struct win))) == 0) {
  1309. nomem:
  1310.     Msg (0, "Out of memory.");
  1311.     return -1;
  1312.     }
  1313.     if ((p->image = (char **)malloc (rows * sizeof (char *))) == 0)
  1314.     goto nomem;
  1315.     for (cp = p->image; cp < p->image+rows; ++cp) {
  1316.     if ((*cp = malloc (cols)) == 0)
  1317.         goto nomem;
  1318.     bclear (*cp, cols);
  1319.     }
  1320.     if ((p->attr = (char **)malloc (rows * sizeof (char *))) == 0)
  1321.     goto nomem;
  1322.     for (cp = p->attr; cp < p->attr+rows; ++cp) {
  1323.     if ((*cp = malloc (cols)) == 0)
  1324.         goto nomem;
  1325.     bzero (*cp, cols);
  1326.     }
  1327.     if ((p->tabs = malloc (cols+1)) == 0)  /* +1 because 0 <= x <= cols */
  1328.     goto nomem;
  1329.     ResetScreen (p);
  1330.     p->aflag = aflag;
  1331.     p->active = 0;
  1332.     p->bell = 0;
  1333.     p->outlen = 0;
  1334.     p->ptyfd = f;
  1335.     strncpy (p->cmd, Filename (args[0]), MAXSTR-1);
  1336.     p->cmd[MAXSTR-1] = '\0';
  1337.     strncpy (p->tty, TtyName, MAXSTR-1);
  1338.     (void) chown (TtyName, getuid (), getgid ());
  1339.     (void) chmod (TtyName, 0622);
  1340.     p->slot = SetUtmp (TtyName);
  1341.     switch (p->wpid = fork ()) {
  1342.     case -1:
  1343.     Msg (errno, "Cannot fork");
  1344.     free (p);
  1345.     return -1;
  1346.     case 0:
  1347.     signal (SIGHUP, SIG_DFL);
  1348.     signal (SIGINT, SIG_DFL);
  1349.     signal (SIGQUIT, SIG_DFL);
  1350.     signal (SIGTERM, SIG_DFL);
  1351.     setuid (getuid ());
  1352.     setgid (getgid ());
  1353.     if (dir && chdir (dir) == -1) {
  1354.         SendErrorMsg ("Cannot chdir to %s: %s", dir, sys_errlist[errno]);
  1355.         exit (1);
  1356.     }
  1357.     mypid = getpid ();
  1358.     if ((f = open ("/dev/tty", O_RDWR)) != -1) {
  1359.         ioctl (f, TIOCNOTTY, (char *)0);
  1360.         close (f);
  1361.     }
  1362.     if ((tf = open (TtyName, O_RDWR)) == -1) {
  1363.         SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
  1364.         exit (1);
  1365.     }
  1366.     dup2 (tf, 0);
  1367.     dup2 (tf, 1);
  1368.     dup2 (tf, 2);
  1369.     for (f = getdtablesize () - 1; f > 2; f--)
  1370.         close (f);
  1371.     ioctl (0, TIOCSPGRP, &mypid);
  1372.     setpgrp (0, mypid);
  1373.     SetTTY (0, &OldMode);
  1374.     NewEnv[2] = MakeTermcap (aflag);
  1375.     sprintf (ebuf, "WINDOW=%d", n);
  1376.     NewEnv[3] = ebuf;
  1377.     execvpe (prog, args, NewEnv);
  1378.     SendErrorMsg ("Cannot exec %s: %s", prog, sys_errlist[errno]);
  1379.     exit (1);
  1380.     }
  1381.     return n;
  1382. }
  1383.  
  1384. execvpe (prog, args, env) char *prog, **args, **env; {
  1385.     register char *path, *p;
  1386.     char buf[1024];
  1387.     char *shargs[MAXARGS+1];
  1388.     register i, eaccess = 0;
  1389.  
  1390.     if (prog[0] == '/')
  1391.     path = "";
  1392.     else if ((path = getenv ("PATH")) == 0)
  1393.     path = DefaultPath;
  1394.     do {
  1395.     p = buf;
  1396.     while (*path && *path != ':')
  1397.         *p++ = *path++;
  1398.     if (p > buf)
  1399.         *p++ = '/';
  1400.     strcpy (p, prog);
  1401.     if (*path)
  1402.         ++path;
  1403.     execve (buf, args, env);
  1404.     switch (errno) {
  1405.     case ENOEXEC:
  1406.         shargs[0] = DefaultShell;
  1407.         shargs[1] = buf;
  1408.         for (i = 1; shargs[i+1] = args[i]; ++i)
  1409.         ;
  1410.         execve (DefaultShell, shargs, env);
  1411.         return;
  1412.     case EACCES:
  1413.         eaccess = 1;
  1414.         break;
  1415.     case ENOMEM: case E2BIG: case ETXTBSY:
  1416.         return;
  1417.     }
  1418.     } while (*path);
  1419.     if (eaccess)
  1420.     errno = EACCES;
  1421. }
  1422.  
  1423. static WriteFile (dump) {   /* dump==0: create .termcap, dump==1: hardcopy */
  1424.     register i, j, k;
  1425.     register char *p;
  1426.     register FILE *f;
  1427.     char fn[1024];
  1428.     int pid, s;
  1429.  
  1430.     if (dump)
  1431.     sprintf (fn, "hardcopy.%d", CurrNum);
  1432.     else
  1433.     sprintf (fn, "%s/%s/.termcap", home, SockDir);
  1434.     switch (pid = fork ()) {
  1435.     case -1:
  1436.     Msg (errno, "fork");
  1437.     return;
  1438.     case 0:
  1439.     setuid (getuid ());
  1440.     setgid (getgid ());
  1441.     if ((f = fopen (fn, "w")) == NULL)
  1442.         exit (1);
  1443.     if (dump) {
  1444.         for (i = 0; i < rows; ++i) {
  1445.         p = curr->image[i];
  1446.         for (k = cols-1; k >= 0 && p[k] == ' '; --k) ;
  1447.         for (j = 0; j <= k; ++j)
  1448.             putc (p[j], f);
  1449.         putc ('\n', f);
  1450.         }
  1451.     } else {
  1452.         if (p = index (MakeTermcap (curr->aflag), '=')) {
  1453.         fputs (++p, f);
  1454.         putc ('\n', f);
  1455.         }
  1456.     }
  1457.     fclose (f);
  1458.     exit (0);
  1459.     default:
  1460.     while ((i = wait (&s)) != pid)
  1461.         if (i == -1) return;
  1462.     if ((s >> 8) & 0377)
  1463.         Msg (0, "Cannot open \"%s\".", fn);
  1464.     else
  1465.         Msg (0, "%s written to \"%s\".", dump ? "Screen image" :
  1466.         "Termcap entry", fn);
  1467.     }
  1468. }
  1469.  
  1470. static ShowWindows () {
  1471.     char buf[1024];
  1472.     register char *s;
  1473.     register struct win **pp, *p;
  1474.  
  1475.     for (s = buf, pp = wtab; pp < wtab+MAXWIN; ++pp) {
  1476.     if ((p = *pp) == 0)
  1477.         continue;
  1478.     if (s - buf + 5 + strlen (p->cmd) > cols-1)
  1479.         break;
  1480.     if (s > buf) {
  1481.         *s++ = ' '; *s++ = ' ';
  1482.     }
  1483.     *s++ = pp - wtab + '0';
  1484.     if (p == curr)
  1485.         *s++ = '*';
  1486.     else if (p == other)
  1487.         *s++ = '-';
  1488.     *s++ = ' ';
  1489.     strcpy (s, p->cmd);
  1490.     s += strlen (s);
  1491.     }
  1492.     Msg (0, buf);
  1493. }
  1494.  
  1495. static ShowInfo () {
  1496.     char buf[1024], *p;
  1497.     register struct win *wp = curr;
  1498.     struct tm *tp;
  1499.     time_t now;
  1500. #ifdef LOADAV
  1501.     double av[3];
  1502. #endif
  1503.  
  1504.     time (&now);
  1505.     tp = localtime (&now);
  1506.     sprintf (buf, "%2d:%02.2d:%02.2d %s", tp->tm_hour, tp->tm_min, tp->tm_sec,
  1507.     HostName);
  1508. #ifdef LOADAV
  1509.     if (avenrun && GetAvenrun (av)) {
  1510.     p = buf + strlen (buf);
  1511.     sprintf (p, " %2.2f %2.2f %2.2f", av[0], av[1], av[2]);
  1512.     }
  1513. #endif
  1514.     p = buf + strlen (buf);
  1515.     sprintf (p, " (%d,%d) %cflow %cins %corg %cwrap %cpad", wp->y, wp->x,
  1516.     flowctl ? '+' : '-',
  1517.     wp->insert ? '+' : '-', wp->origin ? '+' : '-',
  1518.     wp->wrap ? '+' : '-', wp->keypad ? '+' : '-');
  1519.     Msg (0, buf);
  1520. }
  1521.  
  1522. static OpenPTY () {
  1523.     register char *p, *l, *d;
  1524.     register i, f, tf;
  1525.  
  1526.     strcpy (PtyName, PtyProto);
  1527.     strcpy (TtyName, TtyProto);
  1528.     for (p = PtyName, i = 0; *p != 'X'; ++p, ++i) ;
  1529.     for (l = "pqr"; *p = *l; ++l) {
  1530.     for (d = "0123456789abcdef"; p[1] = *d; ++d) {
  1531.         if ((f = open (PtyName, O_RDWR)) != -1) {
  1532.         TtyName[i] = p[0];
  1533.         TtyName[i+1] = p[1];
  1534.         if ((tf = open (TtyName, O_RDWR)) != -1) {
  1535.             close (tf);
  1536.             return f;
  1537.         }
  1538.         close (f);
  1539.         }
  1540.     }
  1541.     }
  1542.     return -1;
  1543. }
  1544.  
  1545. static SetTTY (fd, mp) struct mode *mp; {
  1546.     ioctl (fd, TIOCSETP, &mp->m_ttyb);
  1547.     ioctl (fd, TIOCSETC, &mp->m_tchars);
  1548.     ioctl (fd, TIOCSLTC, &mp->m_ltchars);
  1549.     ioctl (fd, TIOCLSET, &mp->m_lmode);
  1550.     ioctl (fd, TIOCSETD, &mp->m_ldisc);
  1551. }
  1552.  
  1553. static GetTTY (fd, mp) struct mode *mp; {
  1554.     ioctl (fd, TIOCGETP, &mp->m_ttyb);
  1555.     ioctl (fd, TIOCGETC, &mp->m_tchars);
  1556.     ioctl (fd, TIOCGLTC, &mp->m_ltchars);
  1557.     ioctl (fd, TIOCLGET, &mp->m_lmode);
  1558.     ioctl (fd, TIOCGETD, &mp->m_ldisc);
  1559. }
  1560.  
  1561. static SetMode (op, np) struct mode *op, *np; {
  1562.     *np = *op;
  1563.     np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
  1564.     np->m_ttyb.sg_flags |= CBREAK;
  1565.     np->m_tchars.t_intrc = -1;
  1566.     np->m_tchars.t_quitc = -1;
  1567.     if (!flowctl) {
  1568.     np->m_tchars.t_startc = -1;
  1569.     np->m_tchars.t_stopc = -1;
  1570.     }
  1571.     np->m_ltchars.t_suspc = -1;
  1572.     np->m_ltchars.t_dsuspc = -1;
  1573.     np->m_ltchars.t_flushc = -1;
  1574.     np->m_ltchars.t_lnextc = -1;
  1575. }
  1576.  
  1577. static GetSockName () {
  1578.     struct stat s;
  1579.     register client;
  1580.     register char *p;
  1581.  
  1582.     if (!mflag && (SockName = getenv ("STY")) != 0 && *SockName != '\0') {
  1583.     client = 1;
  1584.     setuid (getuid ());
  1585.     setgid (getgid ());
  1586.     } else {
  1587.     if ((p = ttyname (0)) == 0 || (p = ttyname (1)) == 0 ||
  1588.         (p = ttyname (2)) == 0 || *p == '\0')
  1589.         Msg (0, "screen must run on a tty.");
  1590.     SockName = Filename (p);
  1591.     client = 0;
  1592.     }
  1593.     if ((home = getenv ("HOME")) == 0)
  1594.     Msg (0, "$HOME is undefined.");
  1595.     sprintf (SockPath, "%s/%s", home, SockDir);
  1596.     if (stat (SockPath, &s) == -1) {
  1597.     if (errno == ENOENT) {
  1598.         if (mkdir (SockPath, 0700) == -1)
  1599.         Msg (errno, "Cannot make directory %s", SockPath);
  1600.         (void) chown (SockPath, getuid (), getgid ());
  1601.     } else Msg (errno, "Cannot get status of %s", SockPath);
  1602.     } else {
  1603.     if ((s.st_mode & S_IFMT) != S_IFDIR)
  1604.         Msg (0, "%s is not a directory.", SockPath);
  1605.     if ((s.st_mode & 0777) != 0700)
  1606.         Msg (0, "Directory %s must have mode 700.", SockPath);
  1607.     if (s.st_uid != getuid ())
  1608.         Msg (0, "You are not the owner of %s.", SockPath);
  1609.     }
  1610.     strcat (SockPath, "/");
  1611.     strcat (SockPath, SockName);
  1612.     return client;
  1613. }
  1614.  
  1615. static MakeServerSocket () {
  1616.     register s;
  1617.     struct sockaddr_un a;
  1618.  
  1619.     (void) unlink (SockPath);
  1620.     if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
  1621.     Msg (errno, "socket");
  1622.     a.sun_family = AF_UNIX;
  1623.     strcpy (a.sun_path, SockPath);
  1624.     if (bind (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
  1625.     Msg (errno, "bind");
  1626.     (void) chown (SockPath, getuid (), getgid ());
  1627.     if (listen (s, 5) == -1)
  1628.     Msg (errno, "listen");
  1629.     return s;
  1630. }
  1631.  
  1632. static MakeClientSocket () {
  1633.     register s;
  1634.     struct sockaddr_un a;
  1635.  
  1636.     if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
  1637.     Msg (errno, "socket");
  1638.     a.sun_family = AF_UNIX;
  1639.     strcpy (a.sun_path, SockPath);
  1640.     if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
  1641.     Msg (errno, "connect: %s", SockPath);
  1642.     return s;
  1643. }
  1644.  
  1645. static SendCreateMsg (s, ac, av, aflag) char **av; {
  1646.     struct msg m;
  1647.     register char *p;
  1648.     register len, n;
  1649.  
  1650.     m.type = MSG_CREATE;
  1651.     p = m.m.create.line;
  1652.     for (n = 0; ac > 0 && n < MAXARGS-1; ++av, --ac, ++n) {
  1653.     len = strlen (*av) + 1;
  1654.     if (p + len >= m.m.create.line+MAXLINE)
  1655.         break;
  1656.     strcpy (p, *av);
  1657.     p += len;
  1658.     }
  1659.     m.m.create.nargs = n;
  1660.     m.m.create.aflag = aflag;
  1661.     if (getwd (m.m.create.dir) == 0)
  1662.     Msg (0, "%s", m.m.create.dir);
  1663.     if (write (s, &m, sizeof (m)) != sizeof (m))
  1664.     Msg (errno, "write");
  1665. }
  1666.  
  1667. /*VARARGS1*/
  1668. static SendErrorMsg (fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
  1669.     register s;
  1670.     struct msg m;
  1671.  
  1672.     s = MakeClientSocket ();
  1673.     m.type = MSG_ERROR;
  1674.     sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
  1675.     (void) write (s, &m, sizeof (m));
  1676.     close (s);
  1677.     sleep (2);
  1678. }
  1679.  
  1680. static ReceiveMsg (s) {
  1681.     register ns;
  1682.     struct sockaddr_un a;
  1683.     int len = sizeof (a);
  1684.     struct msg m;
  1685.  
  1686.     if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1) {
  1687.     Msg (errno, "accept");
  1688.     return;
  1689.     }
  1690.     if ((len = read (ns, &m, sizeof (m))) != sizeof (m)) {
  1691.     if (len == -1)
  1692.         Msg (errno, "read");
  1693.     else
  1694.         Msg (0, "Short message (%d bytes)", len);
  1695.     close (ns);
  1696.     return;
  1697.     }
  1698.     switch (m.type) {
  1699.     case MSG_CREATE:
  1700.     ExecCreate (&m);
  1701.     break;
  1702.     case MSG_ERROR:
  1703.     Msg (0, "%s", m.m.message);
  1704.     break;
  1705.     default:
  1706.     Msg (0, "Invalid message (type %d).", m.type);
  1707.     }
  1708.     close (ns);
  1709. }
  1710.  
  1711. static ExecCreate (mp) struct msg *mp; {
  1712.     char *args[MAXARGS];
  1713.     register n;
  1714.     register char **pp = args, *p = mp->m.create.line;
  1715.  
  1716.     for (n = mp->m.create.nargs; n > 0; --n) {
  1717.     *pp++ = p;
  1718.     p += strlen (p) + 1;
  1719.     }
  1720.     *pp = 0;
  1721.     if ((n = MakeWindow (mp->m.create.line, args, mp->m.create.aflag, 0,
  1722.         mp->m.create.dir)) != -1)
  1723.     SwitchWindow (n);
  1724. }
  1725.  
  1726. static ReadRc (fn) char *fn; {
  1727.     FILE *f;
  1728.     register char *p, **pp, **ap;
  1729.     register argc, num, c;
  1730.     char buf[256];
  1731.     char *args[MAXARGS];
  1732.     int key;
  1733.  
  1734.     ap = args;
  1735.     if (access (fn, R_OK) == -1)
  1736.     return;
  1737.     if ((f = fopen (fn, "r")) == NULL)
  1738.     return;
  1739.     while (fgets (buf, 256, f) != NULL) {
  1740.     if (p = rindex (buf, '\n'))
  1741.         *p = '\0';
  1742.     if ((argc = Parse (fn, buf, ap)) == 0)
  1743.         continue;
  1744.     if (strcmp (ap[0], "escape") == 0) {
  1745.         p = ap[1];
  1746.         if (argc < 2 || strlen (p) != 2)
  1747.         Msg (0, "%s: two characters required after escape.", fn);
  1748.         Esc = *p++;
  1749.         MetaEsc = *p;
  1750.     } else if (strcmp (ap[0], "chdir") == 0) {
  1751.         p = argc < 2 ? home : ap[1];
  1752.         if (chdir (p) == -1)
  1753.         Msg (errno, "%s", p);
  1754.     } else if (strcmp (ap[0], "bell") == 0) {
  1755.         if (argc != 2) {
  1756.         Msg (0, "%s: bell: one argument required.", fn);
  1757.         } else {
  1758.         if ((BellString = malloc (strlen (ap[1]) + 1)) == 0)
  1759.             Msg (0, "Out of memory.");
  1760.         strcpy (BellString, ap[1]);
  1761.         }
  1762.     } else if (strcmp (ap[0], "screen") == 0) {
  1763.         num = 0;
  1764.         if (argc > 1 && IsNum (ap[1], 10)) {
  1765.         num = atoi (ap[1]);
  1766.         if (num < 0 || num > MAXWIN-1)
  1767.             Msg (0, "%s: illegal screen number %d.", fn, num);
  1768.         --argc; ++ap;
  1769.         }
  1770.         if (argc < 2) {
  1771.         ap[1] = ShellProg; argc = 2;
  1772.         }
  1773.         ap[argc] = 0;
  1774.         (void) MakeWindow (ap[1], ap+1, 0, num, (char *)0);
  1775.     } else if (strcmp (ap[0], "bind") == 0) {
  1776.         p = ap[1];
  1777.         if (argc < 2 || *p == '\0')
  1778.         Msg (0, "%s: key expected after bind.", fn);
  1779.         if (p[1] == '\0') {
  1780.         key = *p;
  1781.         } else if (p[0] == '^' && p[1] != '\0' && p[2] == '\0') {
  1782.         c = p[1];
  1783.         if (isupper (c))
  1784.             p[1] = tolower (c);    
  1785.         key = Ctrl(c);
  1786.         } else if (IsNum (p, 7)) {
  1787.         (void) sscanf (p, "%o", &key);
  1788.         } else {
  1789.         Msg (0,
  1790.             "%s: bind: character, ^x, or octal number expected.", fn);
  1791.         }
  1792.         if (argc < 3) {
  1793.         ktab[key].type = 0;
  1794.         } else {
  1795.         for (pp = KeyNames; *pp; ++pp)
  1796.             if (strcmp (ap[2], *pp) == 0) break;
  1797.         if (*pp) {
  1798.             ktab[key].type = pp-KeyNames+1;
  1799.         } else {
  1800.             ktab[key].type = KEY_CREATE;
  1801.             ktab[key].args = SaveArgs (argc-2, ap+2);
  1802.         }
  1803.         }
  1804.     } else Msg (0, "%s: unknown keyword \"%s\".", fn, ap[0]);
  1805.     }
  1806.     fclose (f);
  1807. }
  1808.  
  1809. static Parse (fn, buf, args) char *fn, *buf, **args; {
  1810.     register char *p = buf, **ap = args;
  1811.     register delim, argc = 0;
  1812.  
  1813.     argc = 0;
  1814.     for (;;) {
  1815.     while (*p && (*p == ' ' || *p == '\t')) ++p;
  1816.     if (*p == '\0' || *p == '#')
  1817.         return argc;
  1818.     if (argc > MAXARGS-1)
  1819.         Msg (0, "%s: too many tokens.", fn);
  1820.     delim = 0;
  1821.     if (*p == '"' || *p == '\'') {
  1822.         delim = *p; *p = '\0'; ++p;
  1823.     }
  1824.     ++argc;
  1825.     *ap = p; ++ap;
  1826.     while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
  1827.         ++p;
  1828.     if (*p == '\0') {
  1829.         if (delim)
  1830.         Msg (0, "%s: Missing quote.", fn);
  1831.         else
  1832.         return argc;
  1833.     }
  1834.     *p++ = '\0';
  1835.     }
  1836. }
  1837.  
  1838. static char **SaveArgs (argc, argv) register argc; register char **argv; {
  1839.     register char **ap, **pp;
  1840.  
  1841.     if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0)
  1842.     Msg (0, "Out of memory.");
  1843.     while (argc--) {
  1844.     if ((*pp = malloc (strlen (*argv)+1)) == 0)
  1845.         Msg (0, "Out of memory.");
  1846.     strcpy (*pp, *argv);
  1847.     ++pp; ++argv;
  1848.     }
  1849.     *pp = 0;
  1850.     return ap;
  1851. }
  1852.  
  1853. static MakeNewEnv () {
  1854.     register char **op, **np = NewEnv;
  1855.     static char buf[MAXSTR];
  1856.  
  1857.     if (strlen (SockName) > MAXSTR-5)
  1858.     SockName = "?";
  1859.     sprintf (buf, "STY=%s", SockName);
  1860.     *np++ = buf;
  1861.     *np++ = Term;
  1862.     np += 2;
  1863.     for (op = environ; *op; ++op) {
  1864.     if (np == NewEnv + MAXARGS - 1)
  1865.         break;
  1866.     if (!IsSymbol (*op, "TERM") && !IsSymbol (*op, "TERMCAP")
  1867.         && !IsSymbol (*op, "STY"))
  1868.         *np++ = *op;
  1869.     }
  1870.     *np = 0;
  1871. }
  1872.  
  1873. static IsSymbol (e, s) register char *e, *s; {
  1874.     register char *p;
  1875.     register n;
  1876.  
  1877.     for (p = e; *p && *p != '='; ++p) ;
  1878.     if (*p) {
  1879.     *p = '\0';
  1880.     n = strcmp (e, s);
  1881.     *p = '=';
  1882.     return n == 0;
  1883.     }
  1884.     return 0;
  1885. }
  1886.  
  1887. /*VARARGS2*/
  1888. Msg (err, fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
  1889.     char buf[1024];
  1890.     register char *p = buf;
  1891.  
  1892.     sprintf (p, fmt, p1, p2, p3, p4, p5, p6);
  1893.     if (err) {
  1894.     p += strlen (p);
  1895.     if (err > 0 && err < sys_nerr)
  1896.         sprintf (p, ": %s", sys_errlist[err]);
  1897.     else
  1898.         sprintf (p, ": Error %d", err);
  1899.     }
  1900.     if (HasWindow) {
  1901.     MakeStatus (buf, curr);
  1902.     } else {
  1903.     printf ("%s\r\n", buf);
  1904.     exit (1);
  1905.     }
  1906. }
  1907.  
  1908. bclear (p, n) char *p; {
  1909.     bcopy (blank, p, n);
  1910. }
  1911.  
  1912. static char *Filename (s) char *s; {
  1913.     register char *p;
  1914.  
  1915.     p = s + strlen (s) - 1;
  1916.     while (p >= s && *p != '/') --p;
  1917.     return ++p;
  1918. }
  1919.  
  1920. static IsNum (s, base) register char *s; register base; {
  1921.     for (base += '0'; *s; ++s)
  1922.     if (*s < '0' || *s > base)
  1923.         return 0;
  1924.     return 1;
  1925. }
  1926.  
  1927. static char *MakeBellMsg (n) {
  1928.     static char buf[MAXSTR];
  1929.     register char *p = buf, *s = BellString;
  1930.  
  1931.     for (s = BellString; *s && p < buf+MAXSTR-1; s++)
  1932.     *p++ = (*s == '%') ? n + '0' : *s;
  1933.     *p = '\0';
  1934.     return buf;
  1935. }
  1936.  
  1937. static InitUtmp () {
  1938.     struct passwd *p;
  1939.  
  1940.     if ((utmpf = open (UtmpName, O_WRONLY)) == -1) {
  1941.     if (errno != EACCES)
  1942.         Msg (errno, UtmpName);
  1943.     return;
  1944.     }
  1945.     if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0') {
  1946.     if ((p = getpwuid (getuid ())) == 0)
  1947.         return;
  1948.     LoginName = p->pw_name;
  1949.     }
  1950.     utmp = 1;
  1951. }
  1952.  
  1953. static SetUtmp (name) char *name; {
  1954.     register char *p;
  1955.     register struct ttyent *tp;
  1956.     register slot = 1;
  1957.     struct utmp u;
  1958.  
  1959.     if (!utmp)
  1960.     return 0;
  1961.     if (p = rindex (name, '/'))
  1962.     ++p;
  1963.     else p = name;
  1964.     setttyent ();
  1965.     while ((tp = getttyent ()) != NULL && strcmp (p, tp->ty_name) != 0)
  1966.     ++slot;
  1967.     if (tp == NULL)
  1968.     return 0;
  1969.     strncpy (u.ut_line, p, 8);
  1970.     strncpy (u.ut_name, LoginName, 8);
  1971.     u.ut_host[0] = '\0';
  1972.     time (&u.ut_time);
  1973.     (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
  1974.     (void) write (utmpf, &u, sizeof (u));
  1975.     return slot;
  1976. }
  1977.  
  1978. static RemoveUtmp (slot) {
  1979.     struct utmp u;
  1980.  
  1981.     if (slot) {
  1982.     bzero (&u, sizeof (u));
  1983.     (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
  1984.     (void) write (utmpf, &u, sizeof (u));
  1985.     }
  1986. }
  1987.  
  1988. #ifndef GETTTYENT
  1989.  
  1990. static setttyent () {
  1991.     struct stat s;
  1992.     register f;
  1993.     register char *p, *ep;
  1994.  
  1995.     if (ttnext) {
  1996.     ttnext = tt;
  1997.     return;
  1998.     }
  1999.     if ((f = open (ttys, O_RDONLY)) == -1 || fstat (f, &s) == -1)
  2000.     Msg (errno, ttys);
  2001.     if ((tt = malloc (s.st_size + 1)) == 0)
  2002.     Msg (0, "Out of memory.");
  2003.     if (read (f, tt, s.st_size) != s.st_size)
  2004.     Msg (errno, ttys);
  2005.     close (f);
  2006.     for (p = tt, ep = p + s.st_size; p < ep; ++p)
  2007.     if (*p == '\n') *p = '\0';
  2008.     *p = '\0';
  2009.     ttnext = tt;
  2010. }
  2011.  
  2012. static struct ttyent *getttyent () {
  2013.     static struct ttyent t;
  2014.  
  2015.     if (*ttnext == '\0')
  2016.     return NULL;
  2017.     t.ty_name = ttnext + 2;
  2018.     ttnext += strlen (ttnext) + 1;
  2019.     return &t;
  2020. }
  2021.  
  2022. #endif
  2023.  
  2024. #ifdef LOADAV
  2025.  
  2026. static InitKmem () {
  2027.     if ((kmemf = open (KmemName, O_RDONLY)) == -1)
  2028.     return;
  2029.     nl[0].n_name = AvenrunSym;
  2030.     nlist (UnixName, nl);
  2031.     if (nl[0].n_type == 0 || nl[0].n_value == 0)
  2032.     return;
  2033.     avenrun = 1;
  2034. }
  2035.  
  2036. static GetAvenrun (av) double av[]; {
  2037.     if (lseek (kmemf, nl[0].n_value, 0) == -1)
  2038.     return 0;
  2039.     if (read (kmemf, av, 3*sizeof (double)) != 3*sizeof (double))
  2040.     return 0;
  2041.     return 1;
  2042. }
  2043.  
  2044. #endif
  2045. SHAR_EOF
  2046. if test 32087 -ne "`wc -c < 'screen.c'`"
  2047. then
  2048.     echo shar: error transmitting "'screen.c'" '(should have been 32087 characters)'
  2049. fi
  2050. fi # end of overwriting check
  2051. #    End of shell archive
  2052. exit 0
  2053.  
  2054. -- 
  2055.  
  2056. Rich $alz            "Anger is an energy"
  2057. Cronus Project, BBN Labs    rsalz@bbn.com
  2058. Moderator, comp.sources.unix    sources@uunet.uu.net
  2059.